/*
 * 版权所有 2009-2023山东新北洋信息技术股份有限公司 保留所有权利。
 */
package online.xuping.thousandsOfWorlds.common.redis;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

/**
 * 分布式锁工具类
 *
 * @author dongyinggang
 * @date 2023/5/24 8:27
 * @copyright 2023 山东新北洋信息技术股份有限公司. All rights reserved
 */
@Configuration
public class DistributedLockUtil {

    /**
     * redis操作超时时间
     */
    private static final int TIMEOUT = 100;
    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    public void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
        RedisSerializer<String> stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        redisTemplate.setHashValueSerializer(stringSerializer);
        this.redisTemplate = redisTemplate;
    }

    /**
     * setIfAbsent 方法是 设置key值，value值为当前线程name
     * 若存在，则设置失败
     * 若不存在，则设置成功，同时设置过期时间
     *
     * @param key   key值
     * @param value value值
     * @param ttl   过期时间
     * @return 设置结果
     * @author dongyinggang
     * @date 2021/9/30 9:28
     */
    public boolean setIfAbsent(String key, Object value, String ttl) {

        String script = "return redis.call('set',KEYS[1],ARGV[1],'NX','EX',ARGV[2])";
        String res = redisTemplate.execute(new DefaultRedisScript<>(script, String.class),
                Collections.singletonList(key), value, ttl);
        redisTemplate.opsForValue().setIfAbsent(key, value.toString(), TIMEOUT, TimeUnit.SECONDS);
        return StringUtils.isNotEmpty(res);
    }

    /**
     * delete 方法是 删除redis中的key
     * 比较value值是否是当前线程的锁，只能解锁自己线程上的锁
     * 防止业务超过ttl导致锁自动释放，解锁时解除别人的锁
     * 若成功删除，则返回值为1
     * 若未删除key，则返回0
     *
     * @param key key值
     * @return 是否删除掉key值
     * @author dongyinggang
     * @date 2021/9/30 9:29
     */
    public long delete(String key, Object value) {
        String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";
        return redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
                Collections.singletonList(key), value);

    }

}
